﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Ratatoskr.Toolbox
{
    public class SpriteCullingGrid<T> : Grid<T>, ISpriteCullingGrid where T : Cell
    {
        protected Rectangle _drawRange;
        public Rectangle DrawRange
        {
            get { return _drawRange; }
        }

        protected Rectangle _updateRange;
        public Rectangle UpdateRange
        {
            get { return _updateRange; }
        }

        protected readonly Vector2 _aspect;
        public Vector2 CellAspect
        {
            get { return _aspect; }
        }

        protected readonly float _constraint;
        public float CellConstraint
        {
            get { return _constraint; }
        }

        protected readonly int _intersect;
        public int CellIntersectRange
        {
            get { return _intersect; }
        }

        protected float _pixelScale;
        public float PixelScale
        {
            get { return _pixelScale; }
            set { _pixelScale = value; }
        }

        public SpriteCullingGrid(float pixelScale = 64, int xMin = 0, int yMin = 0, int xMax = 0, int yMax = 0, string name = null)
            : base(xMin, yMin, xMax, yMax, name)
        {
            _aspect = this[0, 0].Aspect;
            _constraint = this[0, 0].Constraint;
            _intersect = this[0, 0].IntersectRange;
            _pixelScale = pixelScale;
            new SpriteTransform(this);
            new GridCuller(this);
            new RepeaterComponent("GlobalUpdateServer").Attach(this);
            new RepeaterComponent("UpdateServer", false, 1).Attach(this);
            new RepeaterComponent("ColliderServer").Attach(this);
            new ServerComponent("CollisionServer").Attach(this);
            new RepeaterComponent("FinalizeUpdateServer", "UpdateServer", false, 2).Attach(this);
            new SpriteDrawRepeater().Attach(this);
        }

        public SpriteCullingGrid(int xMax = 0, int yMax = 0, string name = null)
            : this(64, 0, 0, xMax, yMax, name)
        {
        }

        public SpriteCullingGrid(string name = null)
            : this(64, 0, 0, 0, 0, name)
        {
        }

        protected override void ExtendConstructCell(T cell, int x, int y)
        {
            new SpriteTransform(cell);
            new RepeaterComponent("GlobalUpdateServer").Attach(cell);
            new CullingCellRepeater(WithinUpdateRange, "UpdateServer").Attach(cell);
            new CullingCellRepeater<ISpriteCullableCollider>(WithinUpdateRange, "ColliderServer").Attach(cell);
            new CullingCellRepeater(WithinUpdateRange, "CollisionServer").Attach(cell);
            new CullingCellRepeater(WithinUpdateRange, "FinalizeUpdateServer").Attach(cell);
            new CullingCellSpriteDrawRepeater(WithinDrawRange).Attach(cell);
        }

        public void CalcDrawRange(GraphicsDevice device)
        {

        }

        public void CalcUpdateRange(GraphicsDevice device)
        {

        }

        public bool WithinDrawRange(int x, int y)
        {
            return _drawRange.Contains(x, y);
        }

        public bool WithinUpdateRange(int x, int y)
        {
            return _updateRange.Contains(x, y);
        }
    }

    interface ISpriteCullingGrid
    {
        Rectangle DrawRange { get; }
        Rectangle UpdateRange { get; }
        Vector2 CellAspect { get; }
        float CellConstraint { get; }
        int CellIntersectRange { get; }
        float PixelScale { get; set; }
        void CalcDrawRange(GraphicsDevice device);
        void CalcUpdateRange(GraphicsDevice device);
        bool WithinDrawRange(int x, int y);
        bool WithinUpdateRange(int x, int y);
    }

    class GridCuller : Component
    {
        public GridCuller(string type, string updateType)
            : base(type)
        {
            AddServerProfile(updateType, CalculateCullingRanges);
        }

        public GridCuller(ParentEntity parent = null)
            : this("GridCuller", "GraphicsDeviceBroadcast")
        {
        }

        protected void CalculateCullingRanges(object sender, ServerComponentEventArgs e)
        {
            GraphicsDevice d = e.Args[0] as GraphicsDevice;
            if (d == null)
                throw new ArgumentException("Expected GraphicsDevice as first event argument.");
            ISpriteCullingGrid g = _parent as ISpriteCullingGrid;
            if (g == null)
                return;
            g.CalcDrawRange(d);
            g.CalcUpdateRange(d);
        }
    }

    class CullingCellRepeater : CullingCellRepeater<Component>
    {
        public CullingCellRepeater(CullingCondition condition, string type, string repeatType, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : base(condition, type, repeatType, open, priority, enabled, single)
        {
        }

        public CullingCellRepeater(CullingCondition condition, string type, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : base(condition, type, open, priority, enabled, single)
        {
        }

    }

    class CullingCellRepeater<T> : RepeaterComponent<T> where T : class
    {
        public delegate bool CullingCondition(int x, int y);
        protected CullingCondition _condition;

        public CullingCellRepeater(CullingCondition condition, string type, string repeatType, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : base(type, repeatType, open, priority, enabled, single)
        {
            _condition = condition;
        }

        public CullingCellRepeater(CullingCondition condition, string type, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : this(condition, type, type, open, priority, enabled, single)
        {
        }

        protected override bool ExtendRepeatConditions()
        {
            Cell c = _parent as Cell;
            if (c == null)
                return false;
            return _condition((int)c.Location.X, (int)c.Location.Y);
        }
    }

    class CullingCellSpriteDrawRepeater : SpriteDrawRepeater<ISpriteCullableDrawable>
    {
        public delegate bool CullingCondition(int x, int y);
        protected CullingCondition _condition;

        public CullingCellSpriteDrawRepeater(CullingCondition condition, string type, string repeatType, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : base(type, repeatType, open, priority, enabled, single)
        {
            _condition = condition;
        }

        public CullingCellSpriteDrawRepeater(CullingCondition condition, string type, bool open = false, float priority = 0, bool enabled = true, bool single = false)
            : this(condition, type, type, open, priority, enabled, single)
        {
        }

        public CullingCellSpriteDrawRepeater(CullingCondition condition, bool open = true, bool enabled = true, float priority = 0, bool single = false)
            : this(condition, "SpriteDrawServer", open, priority, enabled, single)
        {
        }

        protected override bool ExtendRepeatConditions()
        {
            Cell c = _parent as Cell;
            if (c == null)
                return false;
            return _condition((int)c.Location.X, (int)c.Location.Y);
        }
    }

    struct CollisionData
    {
    }

    interface ISpriteCullableCollider
    {
        float ColliderDiagonal { get; }
        CollisionData GetCollisionData();
        void CollideWith(Component target);
    }

    interface ISpriteCullableDrawable
    {
        float DrawableDiagonal { get; }
    }
}